Załadowanie potrzebnych pakietów:
import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.metrics import confusion_matrix, accuracy_score, roc_auc_score, precision_score, recall_score, f1_score
from sklearn.preprocessing import label_binarize
from sklearn.ensemble import GradientBoostingClassifier
import warnings
warnings.filterwarnings("ignore", category=UserWarning)
warnings.filterwarnings("ignore", category=RuntimeWarning)
import dalex as dx
%matplotlib inline
Zaczynam od załadowania dwóch datasetów. Jako, że drugi z nich zaczyna się dopiero od roku 1960, trzeba skrócić pierwszy z nich
imdb = pd.read_csv(r"C:\Users\dell\Desktop\studia uw\2 sem\ons\imdb.csv", sep = ",")
imdb = imdb[imdb['Year'] >= 1960]
imdb
| Film | Year | Position | Edition | id_imdb | rate | rate_count | duration | genres | country | budget | gross | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 220 | The Apartment | 1960 | Winner | 33 | 53604 | 8.3 | 163969.0 | 125.0 | [' Comedy', ' Drama', ' Romance'] | ['USA'] | 3000000.0 | 1.877845e+07 |
| 221 | The Alamo | 1960 | Nominee | 33 | 53580 | 6.9 | 14366.0 | 162.0 | [' Adventure', ' Drama', ' History', ' War', '... | ['USA'] | 12000000.0 | NaN |
| 222 | Elmer Gantry | 1960 | Nominee | 33 | 53793 | 7.8 | 10360.0 | 146.0 | [' Drama'] | ['USA'] | 3000000.0 | NaN |
| 223 | Sons and Lovers | 1960 | Nominee | 33 | 54326 | 7.1 | 1399.0 | 103.0 | [' Drama'] | ['UK'] | 500000.0 | NaN |
| 224 | The Sundowners | 1960 | Nominee | 33 | 54353 | 7.1 | 3769.0 | 133.0 | [' Adventure', ' Drama'] | ['UK', 'USA'] | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 558 | Joker | 2019 | Nominee | 92 | 7286456 | 8.5 | 933010.0 | 122.0 | [' Crime', ' Drama', ' Thriller'] | ['USA', 'Canada'] | 55000000.0 | 1.074251e+09 |
| 559 | Little Women | 2019 | Nominee | 92 | 3281548 | 7.8 | 141728.0 | 135.0 | [' Drama', ' Romance'] | ['USA'] | 40000000.0 | 2.166012e+08 |
| 560 | Marriage Story | 2019 | Nominee | 92 | 7653254 | 7.9 | 244940.0 | 137.0 | [' Comedy', ' Drama', ' Romance'] | ['UK', 'USA'] | 18600000.0 | 3.336860e+05 |
| 561 | 1917 | 2019 | Nominee | 92 | 8579674 | 8.3 | 420872.0 | 119.0 | [' Drama', ' Thriller', ' War'] | ['USA', 'UK', 'India', 'Spain', 'Canada', 'Chi... | 95000000.0 | 3.849118e+08 |
| 562 | Once Upon a Time in Hollywood | 2019 | Nominee | 92 | 7131622 | 7.6 | 547006.0 | 161.0 | [' Comedy', ' Drama'] | ['USA', 'UK', 'China'] | 90000000.0 | 3.743436e+08 |
343 rows × 12 columns
obp = pd.read_csv(r"C:\Users\dell\Desktop\studia uw\2 sem\ons\oscardata_bestpicture.csv", sep = ",")
obp
| Category | Film | Nominee | Winner | Year | Rating_IMDB | Release_date | Rating_rtaudience | Rating_rtcritic | Oscarstat_totalnoms | ... | Nonom_Criticschoice | Nom_Criticschoice | Nowin_SAG_bestcast | Win_SAG_bestcast | Nonom_SAG_bestcast | Nom_SAG_bestcast | Nowin_PGA | Win_PGA | Nonom_PGA | Nom_PGA | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Picture | The Apartment | The Apartment | 1 | 1961 | 8.3 | 1960-09-16 | 94 | 93 | 10 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | Picture | The Alamo | The Alamo | 0 | 1961 | 6.9 | 1960-10-24 | 64 | 50 | 6 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | Picture | Elmer Gantry | Elmer Gantry | 0 | 1961 | 7.9 | 1960-07 | 86 | 96 | 5 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 3 | Picture | Sons and Lovers | Sons and Lovers | 0 | 1961 | 7.3 | 1960-07-22 | 54 | 75 | 7 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | Picture | The Sundowners | The Sundowners | 0 | 1961 | 7.2 | 1961-02-28 | 62 | 78 | 5 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 328 | Picture | A Star Is Born | A Star Is Born | 0 | 2019 | 8.0 | 2018-09-01 | 90 | 81 | 7 | ... | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
| 329 | Picture | The Favourite | The Favourite | 0 | 2019 | 7.9 | 2018-12-01 | 93 | 62 | 10 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |
| 330 | Picture | Vice | Vice | 0 | 2019 | 7.1 | 2018-12-12 | 66 | 55 | 8 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |
| 331 | Picture | Roma | Roma | 0 | 2019 | 8.0 | 2018-11-21 | 96 | 83 | 10 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |
| 332 | Picture | Black Panther | Black Panther | 0 | 2019 | 7.4 | 2018-02-15 | 97 | 79 | 6 | ... | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 |
333 rows × 60 columns
Niektóre nazwy kolumn mogą być niejasne:
Okazuje się, że datasety różnią się wielkością, należy więc znaleźć różnice i je wyeliminować:
lista_a = list(imdb['Film'])
lista_a1 = list(obp['Film'])
roznica = list(set(lista_a) - set(lista_a1))
roznica
['Jojo Rabbit', 'Extremely Loud & Incredibly Close', 'Crouching Tiger, Hidden Dragon', '1917', 'Ford v Ferrari', "Precious: Based on the Novel 'Push' by Sapphire", 'MASH', 'Les Misérables', 'Marriage Story', 'Secrets & Lies', 'Once Upon a Time in Hollywood', 'Parasite', 'The Postman (Il Postino)', 'Little Women', 'Joker', 'The Irishman']
roznica2 = list(set(lista_a1) - set(lista_a))
roznica2
['Precious', 'Crouching Tiger, Hidden Dragon (Wo hu cang long)', 'Il Postino: The Postman', 'M*A*S*H', 'Secrets and Lies', 'Extremely Loud and Incredibly Close']
W przypadku tytułów: 'Once Upon a Time in Hollywood', '1917', 'Joker','Marriage Story', 'Little Women', 'Jojo Rabbit', 'The Irishman', 'Ford v Ferrari', 'Parasite' - różnica wynika z tego że w jendym datasecie w "Year" jest wpisana data nagrody a w drugim data premiery filmu
do_usuniecia = ['Once Upon a Time in Hollywood', '1917', 'Joker',
'Marriage Story', 'Little Women', 'Jojo Rabbit',
'The Irishman', 'Ford v Ferrari', 'Parasite', 'Les Misérables']
for x in lista_a:
if x in do_usuniecia:
idx = imdb.index[imdb['Film']==x].tolist()[0]
imdb = imdb.drop([idx], axis=0)#, inplace=True)
#print(idx)
imdb.tail(10)
| Film | Year | Position | Edition | id_imdb | rate | rate_count | duration | genres | country | budget | gross | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 544 | The Post | 2017 | Nominee | 90 | 6294822 | 7.2 | 135510.0 | 116.0 | [' Drama'] | ['USA', 'UK'] | 50000000.0 | 1.797695e+08 |
| 545 | Three Billboards Outside Ebbing, Missouri | 2017 | Nominee | 90 | 5027774 | 8.2 | 430452.0 | 115.0 | [' Comedy', ' Crime', ' Drama'] | ['UK', 'USA'] | 15000000.0 | 1.601920e+08 |
| 546 | Green Book | 2018 | Winner | 91 | 6966692 | 8.2 | 374447.0 | 130.0 | [' Biography', ' Comedy', ' Drama', ' Music'] | ['USA', 'China'] | 23000000.0 | 3.217527e+08 |
| 547 | Black Panther | 2018 | Nominee | 91 | 1825683 | 7.3 | 623682.0 | 134.0 | [' Action', ' Adventure', ' Sci-Fi'] | ['USA'] | 200000000.0 | 1.347598e+09 |
| 548 | BlacKkKlansman | 2018 | Nominee | 91 | 7349662 | 7.5 | 221961.0 | 135.0 | [' Biography', ' Comedy', ' Crime', ' Drama'] | ['USA'] | 15000000.0 | 9.340082e+07 |
| 549 | Bohemian Rhapsody | 2018 | Nominee | 91 | 1727824 | 8.0 | 448781.0 | 134.0 | [' Biography', ' Drama', ' Music'] | ['UK', 'USA'] | 52000000.0 | 9.052345e+08 |
| 550 | The Favourite | 2018 | Nominee | 91 | 5083738 | 7.5 | 171605.0 | 119.0 | [' Biography', ' Comedy', ' Drama', ' History'] | ['Ireland', 'UK', 'USA'] | 15000000.0 | 9.591871e+07 |
| 551 | Roma | 2018 | Nominee | 91 | 6155172 | 7.7 | 139905.0 | 135.0 | [' Drama'] | ['Mexico', 'USA'] | NaN | 1.140769e+06 |
| 552 | A Star Is Born | 2018 | Nominee | 91 | 1517451 | 7.6 | 333071.0 | 136.0 | [' Drama', ' Music', ' Romance'] | ['USA'] | 36000000.0 | 4.361889e+08 |
| 553 | Vice | 2018 | Nominee | 91 | 6266538 | 7.2 | 121358.0 | 132.0 | [' Biography', ' Comedy', ' Drama'] | ['USA'] | 60000000.0 | 7.607349e+07 |
W pozostałych przypadkach różnica wynika z różnego zapisu tytułu, poprawiam więc je ręcznie:
idx = imdb.index[imdb['Film']=="Precious: Based on the Novel 'Push' by Sapphire"].tolist()[0]
imdb.at[idx,'Film'] = 'Precious'
idx = imdb.index[imdb['Film']=='The Postman (Il Postino)'].tolist()[0]
imdb.at[idx,'Film'] = 'Il Postino: The Postman'
idx = imdb.index[imdb['Film']=='Secrets & Lies'].tolist()[0]
imdb.at[idx,'Film'] = 'Secrets and Lies'
idx = imdb.index[imdb['Film']=='MASH'].tolist()[0]
imdb.at[idx,'Film'] = 'M*A*S*H'
idx = imdb.index[imdb['Film']=='Crouching Tiger, Hidden Dragon'].tolist()[0]
imdb.at[idx,'Film'] = 'Crouching Tiger, Hidden Dragon (Wo hu cang long)'
idx = imdb.index[imdb['Film']=='Extremely Loud & Incredibly Close'].tolist()[0]
imdb.at[idx,'Film'] = 'Extremely Loud and Incredibly Close'
print(len(obp), len(imdb)) # teraz już rozmiary się zgadzają
333 333
Usuwam zbędne koumny:
obp.drop(['Category', 'Nominee'], axis=1, inplace=True)
imdb.drop(['Year'], axis=1, inplace=True)
imdb = imdb.set_index('Film')
obp = obp.set_index('Film')
Następnie łączę DataFrame'y na podstawie nazwy filmu:
data = pd.merge(imdb, obp, left_index=True, right_index=True)
data
| Position | Edition | id_imdb | rate | rate_count | duration | genres | country | budget | gross | ... | Nonom_Criticschoice | Nom_Criticschoice | Nowin_SAG_bestcast | Win_SAG_bestcast | Nonom_SAG_bestcast | Nom_SAG_bestcast | Nowin_PGA | Win_PGA | Nonom_PGA | Nom_PGA | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Film | |||||||||||||||||||||
| The Apartment | Winner | 33 | 53604 | 8.3 | 163969.0 | 125.0 | [' Comedy', ' Drama', ' Romance'] | ['USA'] | 3000000.0 | 18778454.0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Alamo | Nominee | 33 | 53580 | 6.9 | 14366.0 | 162.0 | [' Adventure', ' Drama', ' History', ' War', '... | ['USA'] | 12000000.0 | NaN | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Elmer Gantry | Nominee | 33 | 53793 | 7.8 | 10360.0 | 146.0 | [' Drama'] | ['USA'] | 3000000.0 | NaN | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Sons and Lovers | Nominee | 33 | 54326 | 7.1 | 1399.0 | 103.0 | [' Drama'] | ['UK'] | 500000.0 | NaN | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Sundowners | Nominee | 33 | 54353 | 7.1 | 3769.0 | 133.0 | [' Adventure', ' Drama'] | ['UK', 'USA'] | NaN | NaN | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| Bohemian Rhapsody | Nominee | 91 | 1727824 | 8.0 | 448781.0 | 134.0 | [' Biography', ' Drama', ' Music'] | ['UK', 'USA'] | 52000000.0 | 905234477.0 | ... | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
| The Favourite | Nominee | 91 | 5083738 | 7.5 | 171605.0 | 119.0 | [' Biography', ' Comedy', ' Drama', ' History'] | ['Ireland', 'UK', 'USA'] | 15000000.0 | 95918706.0 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |
| Roma | Nominee | 91 | 6155172 | 7.7 | 139905.0 | 135.0 | [' Drama'] | ['Mexico', 'USA'] | NaN | 1140769.0 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |
| A Star Is Born | Nominee | 91 | 1517451 | 7.6 | 333071.0 | 136.0 | [' Drama', ' Music', ' Romance'] | ['USA'] | 36000000.0 | 436188866.0 | ... | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
| Vice | Nominee | 91 | 6266538 | 7.2 | 121358.0 | 132.0 | [' Biography', ' Comedy', ' Drama'] | ['USA'] | 60000000.0 | 76073488.0 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |
333 rows × 67 columns
data.drop(['Year', 'Position', 'Edition', 'id_imdb', 'genres', 'rate',
'Release_date', 'MPAA_rating'], axis=1, inplace=True)
Zapis dotyczący krajów produkcji należy zmienić. Uznałam, że najlepiej będzie zrobić podobnie jak jest w przypadku gatunków filmowych, tzn. dodajemy tyle kolumn ile jest krajów i w zależności od tego, czy dany kraj był pierwotnie wymieniony, dane pole jest wypełnione wartością 1 lub 0.
kraje = list(data['country'])
kraje[4]
"['UK', 'USA']"
(Proszę wybaczyć zapis kodu, ale wykonanie tego ręcznie na tę chwilę okazało się dla mnie najszybsze)
USA = []
UK = []
Switzerland = []
Greece = []
Italy = []
France = []
Sweden = []
Canada = []
India = []
Mexico = []
Japan = []
Ireland = []
New_Zealand = []
Australia = []
Germany = []
Hong_Kong = []
China = []
for a in kraje:
try:
a.index('USA')
except ValueError:
USA.append(0)
else:
USA.append(1)
try:
a.index('UK')
except ValueError:
UK.append(0)
else:
UK.append(1)
try:
a.index('Switzerland')
except ValueError:
Switzerland.append(0)
else:
Switzerland.append(1)
try:
a.index('Greece')
except ValueError:
Greece.append(0)
else:
Greece.append(1)
try:
a.index('Italy')
except ValueError:
Italy.append(0)
else:
Italy.append(1)
try:
a.index('France')
except ValueError:
France.append(0)
else:
France.append(1)
try:
a.index('Sweden')
except ValueError:
Sweden.append(0)
else:
Sweden.append(1)
try:
a.index('Canada')
except ValueError:
Canada.append(0)
else:
Canada.append(1)
try:
a.index('Japan')
except ValueError:
Japan.append(0)
else:
Japan.append(1)
try:
a.index('Ireland')
except ValueError:
Ireland.append(0)
else:
Ireland.append(1)
try:
a.index('New Zealand')
except ValueError:
New_Zealand.append(0)
else:
New_Zealand.append(1)
try:
a.index('Australia')
except ValueError:
Australia.append(0)
else:
Australia.append(1)
try:
a.index('Germany')
except ValueError:
Germany.append(0)
else:
Germany.append(1)
try:
a.index('Hong Kong')
except ValueError:
Hong_Kong.append(0)
else:
Hong_Kong.append(1)
try:
a.index('China')
except ValueError:
China.append(0)
else:
China.append(1)
try:
a.index('India')
except ValueError:
India.append(0)
else:
India.append(1)
try:
a.index('Mexico')
except ValueError:
Mexico.append(0)
else:
Mexico.append(1)
len(New_Zealand)
333
data['prod_USA'] = USA
data['prod_UK'] = UK
data['prod_Switzerland'] = Switzerland
data['prod_Greece'] = Greece
data['prod_Italy'] = Italy
data['prod_France'] = France
data['prod_Sweden'] = Sweden
data['prod_Canada'] = Canada
data['prod_India'] = India
data['prod_Mexico'] = Mexico
data['prod_Japan'] = Japan
data['prod_Ireland'] = Ireland
data['prod_New_Zealand'] = New_Zealand
data['prod_Australia'] = Australia
data['prod_Germany'] = Germany
data['prod_Hong_Kong'] = Hong_Kong
data['prod_China'] = China
data.drop(['country'], axis=1, inplace=True)
data.head(8)
| rate_count | duration | budget | gross | Winner | Rating_IMDB | Rating_rtaudience | Rating_rtcritic | Oscarstat_totalnoms | Release_Q1 | ... | prod_Canada | prod_India | prod_Mexico | prod_Japan | prod_Ireland | prod_New_Zealand | prod_Australia | prod_Germany | prod_Hong_Kong | prod_China | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Film | |||||||||||||||||||||
| The Apartment | 163969.0 | 125.0 | 3000000.0 | 18778454.0 | 1 | 8.3 | 94 | 93 | 10 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Alamo | 14366.0 | 162.0 | 12000000.0 | NaN | 0 | 6.9 | 64 | 50 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Elmer Gantry | 10360.0 | 146.0 | 3000000.0 | NaN | 0 | 7.9 | 86 | 96 | 5 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Sons and Lovers | 1399.0 | 103.0 | 500000.0 | NaN | 0 | 7.3 | 54 | 75 | 7 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Sundowners | 3769.0 | 133.0 | NaN | NaN | 0 | 7.2 | 62 | 78 | 5 | 1 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| West Side Story | 96832.0 | 153.0 | 6000000.0 | 44061777.0 | 1 | 7.6 | 84 | 94 | 11 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Fanny | 1943.0 | 134.0 | NaN | NaN | 0 | 7.1 | 94 | 100 | 5 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Guns of Navarone | 45431.0 | 158.0 | 6000000.0 | NaN | 0 | 7.6 | 86 | 95 | 7 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
8 rows × 75 columns
Następnie sprawdzam ile jest wartości NaN. Jako, że nie jest ich dużo, zamieniam je wartością średnią w danej kolumnie:
a = data.isna().sum()
A=pd.DataFrame(a)
with pd.option_context('display.max_rows', None, 'display.max_columns', None): # more options can be specified also
print(A)
0 rate_count 0 duration 0 budget 28 gross 35 Winner 0 Rating_IMDB 0 Rating_rtaudience 0 Rating_rtcritic 0 Oscarstat_totalnoms 0 Release_Q1 0 Release_Q2 0 Release_Q3 0 Release_Q4 0 Nom_Oscar_bestdirector 0 Nom_DGA 0 Nom_BAFTA 0 Win_DGA 0 Win_BAFTA 0 Nom_GoldenGlobe_bestcomedy 0 Nom_GoldenGlobe_bestdrama 0 Win_GoldenGlobe_bestcomedy 0 Win_GoldenGlobe_bestdrama 0 Genre_action 0 Genre_biography 0 Genre_crime 0 Genre_comedy 0 Genre_drama 0 Genre_horror 0 Genre_fantasy 0 Genre_sci-fi 0 Genre_mystery 0 Genre_music 0 Genre_romance 0 Genre_history 0 Genre_war 0 Genre_filmnoir 0 Genre_thriller 0 Genre_adventure 0 Genre_family 0 Genre_sport 0 Genre_western 0 MPAA_G 0 MPAA_PG 0 MPAA_PG-13 0 MPAA_R 0 MPAA_NC-17 0 Nowin_Criticschoice 0 Win_Criticschoice 0 Nonom_Criticschoice 0 Nom_Criticschoice 0 Nowin_SAG_bestcast 0 Win_SAG_bestcast 0 Nonom_SAG_bestcast 0 Nom_SAG_bestcast 0 Nowin_PGA 0 Win_PGA 0 Nonom_PGA 0 Nom_PGA 0 prod_USA 0 prod_UK 0 prod_Switzerland 0 prod_Greece 0 prod_Italy 0 prod_France 0 prod_Sweden 0 prod_Canada 0 prod_India 0 prod_Mexico 0 prod_Japan 0 prod_Ireland 0 prod_New_Zealand 0 prod_Australia 0 prod_Germany 0 prod_Hong_Kong 0 prod_China 0
data['gross'] = data['gross'].fillna(data['gross'].mean())
data['budget'] = data['budget'].fillna(data['budget'].mean())
data.head(8)
| rate_count | duration | budget | gross | Winner | Rating_IMDB | Rating_rtaudience | Rating_rtcritic | Oscarstat_totalnoms | Release_Q1 | ... | prod_Canada | prod_India | prod_Mexico | prod_Japan | prod_Ireland | prod_New_Zealand | prod_Australia | prod_Germany | prod_Hong_Kong | prod_China | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Film | |||||||||||||||||||||
| The Apartment | 163969.0 | 125.0 | 3.000000e+06 | 1.877845e+07 | 1 | 8.3 | 94 | 93 | 10 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Alamo | 14366.0 | 162.0 | 1.200000e+07 | 1.844227e+08 | 0 | 6.9 | 64 | 50 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Elmer Gantry | 10360.0 | 146.0 | 3.000000e+06 | 1.844227e+08 | 0 | 7.9 | 86 | 96 | 5 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Sons and Lovers | 1399.0 | 103.0 | 5.000000e+05 | 1.844227e+08 | 0 | 7.3 | 54 | 75 | 7 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Sundowners | 3769.0 | 133.0 | 2.808740e+07 | 1.844227e+08 | 0 | 7.2 | 62 | 78 | 5 | 1 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| West Side Story | 96832.0 | 153.0 | 6.000000e+06 | 4.406178e+07 | 1 | 7.6 | 84 | 94 | 11 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Fanny | 1943.0 | 134.0 | 2.808740e+07 | 1.844227e+08 | 0 | 7.1 | 94 | 100 | 5 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| The Guns of Navarone | 45431.0 | 158.0 | 6.000000e+06 | 1.844227e+08 | 0 | 7.6 | 86 | 95 | 7 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
8 rows × 75 columns
Wczytuję dane dotyczące filmów z 2021 roku - jeden z datasetów zawierał informację o nich, a drugi nie - brakujące kolumny wypełniłam sama na podstawie informacji znalezionych w Internecie.
X_test_2021_b = pd.read_excel(r"C:\Users\dell\Desktop\studia uw\2 sem\ons\test_2021_b.xlsx")
tabela = X_test_2021_b[['Film', 'Winner']].set_index('Film')
X_test_2021_b.drop(['Film'], axis=1, inplace=True)
X_test_2021_b
| rate_count | duration | budget | gross | Winner | Rating_IMDB | Rating_rtaudience | Rating_rtcritic | Oscarstat_totalnoms | Release_Q1 | ... | prod_Canada | prod_India | prod_Mexico | prod_Japan | prod_Ireland | prod_New_Zealand | prod_Australia | prod_Germany | prod_Hong_Kong | prod_China | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 157234 | 97 | 6000000 | 28400000 | 0 | 8.3 | 90 | 98 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 81830 | 126 | 26000000 | 7400000 | 0 | 7.6 | 95 | 96 | 6 | 1 | ... | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 76604 | 131 | 25000000 | 122252 | 0 | 7.0 | 60 | 83 | 10 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 3 | 83249 | 115 | 2000000 | 15500000 | 0 | 7.6 | 88 | 98 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 163698 | 107 | 5000000 | 39500000 | 1 | 7.5 | 82 | 94 | 6 | 1 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 5 | 176801 | 113 | 10950000 | 18900000 | 0 | 7.5 | 88 | 91 | 5 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 6 | 130709 | 120 | 5400000 | 516520 | 0 | 7.8 | 91 | 97 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 179565 | 129 | 35000000 | 115709 | 0 | 7.8 | 91 | 89 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
8 rows × 75 columns
Następnie łączę oba DataFrame'y, aby następnie wykonać skalowanie. Po skalowaniu ponownie rozdzielam DataFrame'y.
data_sc = data.copy()
data_sc = data_sc.reset_index(drop=True)
frames = [data_sc, X_test_2021_b]
result = pd.concat(frames)
result = result.reset_index(drop=True)
result.tail(10)
| rate_count | duration | budget | gross | Winner | Rating_IMDB | Rating_rtaudience | Rating_rtcritic | Oscarstat_totalnoms | Release_Q1 | ... | prod_Canada | prod_India | prod_Mexico | prod_Japan | prod_Ireland | prod_New_Zealand | prod_Australia | prod_Germany | prod_Hong_Kong | prod_China | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 331 | 333071.0 | 136.0 | 36000000.0 | 436188866.0 | 0 | 8.0 | 90 | 81 | 7 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 332 | 121358.0 | 132.0 | 60000000.0 | 76073488.0 | 0 | 7.1 | 66 | 55 | 8 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 333 | 157234.0 | 97.0 | 6000000.0 | 28400000.0 | 0 | 8.3 | 90 | 98 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 334 | 81830.0 | 126.0 | 26000000.0 | 7400000.0 | 0 | 7.6 | 95 | 96 | 6 | 1 | ... | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 335 | 76604.0 | 131.0 | 25000000.0 | 122252.0 | 0 | 7.0 | 60 | 83 | 10 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 336 | 83249.0 | 115.0 | 2000000.0 | 15500000.0 | 0 | 7.6 | 88 | 98 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 337 | 163698.0 | 107.0 | 5000000.0 | 39500000.0 | 1 | 7.5 | 82 | 94 | 6 | 1 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 338 | 176801.0 | 113.0 | 10950000.0 | 18900000.0 | 0 | 7.5 | 88 | 91 | 5 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 339 | 130709.0 | 120.0 | 5400000.0 | 516520.0 | 0 | 7.8 | 91 | 97 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 340 | 179565.0 | 129.0 | 35000000.0 | 115709.0 | 0 | 7.8 | 91 | 89 | 6 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
10 rows × 75 columns
msc = MinMaxScaler()
result = pd.DataFrame(msc.fit_transform(result),columns=result.columns)
a = result.tail(8)
a = a.drop(['Winner'], axis=1)
result.drop(result.tail(8).index,inplace=True)
data_sc = result.copy()
Sprawdźmy korelacje pomiędzy cechami opisującymi filmy:
feature_cols = data_sc.columns
feature_cols = feature_cols.drop('Winner')
corr_values = data_sc[feature_cols].corr()
# usuwam wartości poniżej diagonali
tril_index = np.tril_indices_from(corr_values)
for coord in zip(*tril_index):
corr_values.iloc[coord[0], coord[1]] = np.NaN
# zapisuję tworząc data frame
corr_values = (corr_values
.stack()
.to_frame()
.reset_index()
.rename(columns={'level_0':'cecha1',
'level_1':'cecha2',
0:'korelacja'}))
# dodaję kolumnę z wartością absolutną
corr_values['abs_korelacja'] = corr_values.korelacja.abs()
corr_values.sort_values('abs_korelacja', ascending=False).head(10)
| cecha1 | cecha2 | korelacja | abs_korelacja | |
|---|---|---|---|---|
| 2153 | Nowin_Criticschoice | Nowin_SAG_bestcast | 0.777389 | 0.777389 |
| 278 | Rating_IMDB | Rating_rtaudience | 0.747335 | 0.747335 |
| 2231 | Nom_Criticschoice | Nowin_SAG_bestcast | 0.734076 | 0.734076 |
| 2157 | Nowin_Criticschoice | Nowin_PGA | 0.733882 | 0.733882 |
| 2152 | Nowin_Criticschoice | Nom_Criticschoice | 0.706907 | 0.706907 |
| 2259 | Nowin_SAG_bestcast | Nowin_PGA | 0.706771 | 0.706771 |
| 141 | budget | gross | 0.705728 | 0.705728 |
| 1072 | Nom_GoldenGlobe_bestcomedy | Win_GoldenGlobe_bestcomedy | 0.704463 | 0.704463 |
| 3 | rate_count | Rating_IMDB | 0.683197 | 0.683197 |
| 2257 | Nowin_SAG_bestcast | Nonom_SAG_bestcast | 0.665667 | 0.665667 |
Jak widać najbardziej powiązane są ze sobą zdobyte nagrody czy też raczej brak nagród, co nie wydaje się mieć wielkiego znaczenia (zazwyczaj zwycięzców jest zdecydowanie mniej niż nominowanych). Warto również zauważyć wysoką korelację między Rating_IMDB a Rating_rtaudience, co wskazuje na zgodność internautów z portali IMDB i Rotten Tomatoes. Dość mocno powiązane ze sobą są również budżet i zysk ze sprzedaży biletów, jednak myślę, że wynika to raczej z okresu kiedy film powstał niż z ogólnej zależności (ciężko porównywać 1:1 zyski teraz i np. 40 lat temu).
Następnie rozdzielam DataFrame (bez 2021) na zestaw do trenowania i testowania:
feature_cols = data_sc.columns
feature_cols = feature_cols.drop('Winner')
data_sc = data_sc.reset_index(drop=True)
# Get the split indexes
strat_shuf_split = StratifiedShuffleSplit(n_splits=1,
test_size=0.1,
random_state=42)
train_idx, test_idx = next(strat_shuf_split.split(data_sc[feature_cols], data_sc.Winner))
# Create the dataframes
X_train = data_sc.loc[train_idx, feature_cols]
y_train = data_sc.loc[train_idx, 'Winner']
X_test = data_sc.loc[test_idx, feature_cols]
y_test = data_sc.loc[test_idx, 'Winner']
X_train
| rate_count | duration | budget | gross | Rating_IMDB | Rating_rtaudience | Rating_rtcritic | Oscarstat_totalnoms | Release_Q1 | Release_Q2 | ... | prod_Canada | prod_India | prod_Mexico | prod_Japan | prod_Ireland | prod_New_Zealand | prod_Australia | prod_Germany | prod_Hong_Kong | prod_China | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 327 | 0.094420 | 0.432203 | 0.062342 | 0.033472 | 0.419355 | 0.931818 | 0.735294 | 0.400000 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 300 | 0.153939 | 0.389831 | 0.117250 | 0.047821 | 0.516129 | 0.772727 | 0.823529 | 0.333333 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 9 | 0.029066 | 0.805085 | 0.011657 | 0.000004 | 0.677419 | 0.886364 | 0.852941 | 0.733333 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 256 | 0.148143 | 0.084746 | 0.075013 | 0.021767 | 0.451613 | 0.704545 | 0.897059 | 0.333333 | 1.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 142 | 0.026999 | 0.296610 | 0.058118 | 0.012425 | 0.483871 | 0.659091 | 0.897059 | 0.466667 | 1.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 284 | 0.179307 | 0.423729 | 0.231289 | 0.078408 | 0.516129 | 0.795455 | 0.897059 | 0.400000 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 91 | 0.004681 | 0.364407 | 0.011657 | 0.011702 | 0.354839 | 0.659091 | 0.720588 | 0.533333 | 1.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 221 | 0.142273 | 0.728814 | 0.463592 | 0.076590 | 0.419355 | 0.568182 | 0.808824 | 0.733333 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 |
| 287 | 0.230250 | 0.355932 | 0.096131 | 0.017387 | 0.580645 | 0.636364 | 0.926471 | 0.266667 | 1.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 166 | 0.113803 | 0.389831 | 0.184829 | 0.132193 | 0.516129 | 0.795455 | 0.941176 | 0.466667 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
299 rows × 74 columns
Wybieram model GradientBoostingClassifier, jako, że większość danych jakie mamy to dane kategoryczne, ale mamy też kilka kolumn z danymi ciągłymi. Model ten łączy w sobie kilka "mniejszych" modeli, by stworzyć jeden lepszy, bardziej dokładny. Wykonuję iteracje po różnych paramtetrach i znajduję takie parametry, którym odpowiada największa dokładność. Następnie dla znalezionych parametrów trenuję model.
def measure_error(y_true, y_pred, label):
return pd.Series({'accuracy':accuracy_score(y_true, y_pred),'precision': precision_score(y_true, y_pred),
'recall': recall_score(y_true, y_pred),'f1': f1_score(y_true, y_pred)},name=label)
error_list = list()
# Iteracje po różnych wartościach parametrów
tree_list = [50, 75, 100, 150, 200, 250, 400, 450, 500]
learning_list=[0.01, 0.05, 0.075, 0.1, 0.15, 0.2]
max_features_list = [1, 2, 3]
for mf in max_features_list:
for lear in learning_list:
for n_trees in tree_list:
GBC = GradientBoostingClassifier(learning_rate=lear,n_estimators=n_trees,
subsample=0.5,
max_features=mf,
random_state=42)
# Dopasowanie modelu
GBC.fit(X_train.values, y_train.values)
y_pred_1 = GBC.predict(X_test)
# dokładność
ac = accuracy_score(y_test, y_pred_1)
error_list.append(pd.Series({'n_trees': n_trees, 'accuracy': ac, 'learning_rate': lear, 'max_features': mf}))
error_df = pd.concat(error_list, axis=1)
error_df
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| n_trees | 50.000000 | 75.000000 | 100.000000 | 150.000000 | 200.000000 | 250.000000 | 400.000000 | 450.000000 | 500.000000 | 50.000000 | ... | 500.000000 | 50.000000 | 75.000000 | 100.000000 | 150.000000 | 200.000000 | 250.000000 | 400.000000 | 450.000000 | 500.000000 |
| accuracy | 0.823529 | 0.823529 | 0.823529 | 0.823529 | 0.823529 | 0.823529 | 0.882353 | 0.882353 | 0.882353 | 0.852941 | ... | 0.882353 | 0.970588 | 0.911765 | 0.911765 | 0.941176 | 0.911765 | 0.911765 | 0.911765 | 0.911765 | 0.911765 |
| learning_rate | 0.010000 | 0.010000 | 0.010000 | 0.010000 | 0.010000 | 0.010000 | 0.010000 | 0.010000 | 0.010000 | 0.050000 | ... | 0.150000 | 0.200000 | 0.200000 | 0.200000 | 0.200000 | 0.200000 | 0.200000 | 0.200000 | 0.200000 | 0.200000 |
| max_features | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | ... | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 |
4 rows × 162 columns
maxValue=error_df.iloc[1,:].max()
maxValue
0.9705882352941176
error_df.columns[error_df.eq(maxValue).any()]
Int64Index([153], dtype='int64')
error_df.iloc[:,153]
n_trees 50.000000 accuracy 0.970588 learning_rate 0.200000 max_features 3.000000 Name: 153, dtype: float64
Sprawdzam, czy model potrafi znaleźć zwycięzcę Oscarów 2021:
while True:
GBC = GradientBoostingClassifier(learning_rate=0.2,
max_features=3, subsample=0.5,
n_estimators=50)
GBC = GBC.fit(X_train, y_train)
y_pred_gbc_opt = GBC.predict(a)
if 1 not in (y_pred_gbc_opt):
continue
else:
tabela['Predykcja'] = y_pred_gbc_opt
print(tabela)
break
Winner Predykcja Film The Father 0 0.0 Judas and the Black Messiah 0 0.0 Mank 0 0.0 Minari 0 0.0 Nomadland 1 1.0 Promising Young Woman 0 0.0 Sound of Metal 0 0.0 The Trial of the Chicago 7 0 0.0
Pętla while True wynika z tego, że choć zazwyczaj model znajdował zwycięzcę (prawidłowo) to dość często model uznawał, że wśród filmów z 2021 roku nie wygrał żaden. Myślę, że nie będzie wielkim oszustwem, jeśli założę, że chociaż jeden film musi wygrać - predykcja więc jest powtarzana do momentu, gdy chociaż jeden film będzie miał przypisaną wartość 1. W takiej sytuacji, przy optymalnych paramerach, model zawsze wskazuje Nomadland jako zwycięzcę.
Sprawdzam dokładność modelu na większej liczbie danych niż tylko z jednego roku (według wcześniejszego podziału na train i test):
y_pred_gbc_opt2 = GBC.predict(X_test)
dopasowanie_GBC = pd.DataFrame(measure_error(y_test, y_pred_gbc_opt2, 'dopasowanie'))
dopasowanie_GBC
| dopasowanie | |
|---|---|
| accuracy | 0.941176 |
| precision | 1.000000 |
| recall | 0.666667 |
| f1 | 0.800000 |
Dokładność modelu na poziomie 94% wydaje się być zadowalająca, szczególnie, że wszystkie filmy określone przez model jako wygrane, w rzeczywistości faktycznie wygrały (precision = 1). Niestety jakieś z wygranych w rzeczywistości filmów nie zostały zidentyfikowane (recall i w rezultacie f1). Prawdopodobnie wynika to z dużej przewagi filmów, które Oscara nie zdobyły w datasecie oraz z faktu, że zestaw do trenowania jest stosunkowo niewielki (299 wierszy)
sns.set_context('talk')
cm = confusion_matrix(y_test,y_pred_gbc_opt2)
ax = sns.heatmap(cm, annot=True, fmt='d')
ax.set_xlabel('Predykcja');
ax.set_ylabel('Faktyczne wartości')
Text(22.5, 0.5, 'Faktyczne wartości')
Tak więc 2/6 z wygranych filmów nie zostało przewidziane jako wygrane.
Trochę informacji o modelu:
exp = dx.Explainer(GBC, X_train, y_train)
Preparation of a new explainer is initiated -> data : 299 rows 74 cols -> target variable : Parameter 'y' was a pandas.Series. Converted to a numpy.ndarray. -> target variable : 299 values -> model_class : sklearn.ensemble._gb.GradientBoostingClassifier (default) -> label : Not specified, model's class short name will be used. (default) -> predict function : <function yhat_proba_default at 0x0000023A9B386708> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.00139, mean = 0.175, max = 0.982 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -0.578, mean = -0.00152, max = 0.857 -> model_info : package sklearn A new explainer has been created!
Zobaczmy, które cechy miały największy wpływ na decyzję czy dany film wygra Oscara:
model = GBC
feature_imp = pd.Series(model.feature_importances_, index=feature_cols).sort_values(ascending=False)
f1 = feature_imp.head(10)
ax = f1.plot(kind='bar')
ax.set(ylabel='Znaczenie')
[Text(0, 0.5, 'Znaczenie')]
Z wykresu wynika, że największe znaczenie miała informacja, czy film wygrał Nagrodę Amerykańskiej Gildii Reżyserów Filmowych. Wpływ tej cechy wydaje się być bardzo duży, a także mieć dużą przewagę nad pozostałymi cechami. Znaczenie cech z danymi kategorycznymi mogą być jednak w tej metodzie zawyżone, spróbujmy więc sprawdzić jak duży wpływ miała dana cecha za pomocą pakietu dalex, gdzie wykorzystana jest metoda bazująca na permutacji cech, tzn, sprawdzane jest jak bardzo zmieni się dokładność modelu gdy zostanie usunięty wpływ danej zmiennej.
vi = exp.model_parts()
vi.plot(max_vars=5) #wykres A
Mamy potwierdzenie, że zdecydowanie największy wpływ ma Nagroda Gildii, a następnie liczba nominacji do Oscarów. Choć zmieniły się pozycje kolejnych cech - różnice między kolejnymi pozycjami nie są już aż tak duże jak między pierwszym a drugim miejscem.
Weźmy teraz film, który wygrał Oscara w 2021 roku, czyli Nomadland
Nomadland = a.iloc[4,:]
Nomadland = pd.DataFrame(Nomadland).T
Nomadland = Nomadland.reset_index(drop=True)
Nomadland
| rate_count | duration | budget | gross | Rating_IMDB | Rating_rtaudience | Rating_rtcritic | Oscarstat_totalnoms | Release_Q1 | Release_Q2 | ... | prod_Canada | prod_India | prod_Mexico | prod_Japan | prod_Ireland | prod_New_Zealand | prod_Australia | prod_Germany | prod_Hong_Kong | prod_China | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.069479 | 0.194915 | 0.020105 | 0.014155 | 0.419355 | 0.636364 | 0.911765 | 0.4 | 1.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1 rows × 74 columns
exp.predict(Nomadland)
array([0.81732065])
Sprawdzę, które cechy miały największy (oraz 'pozytywny' czy 'negatywny') wpływ na przewidywania dla filmu 'Nomadlad'. Wybieram dwie metody - breakDown oraz Shapley Values, ponieważ obie można użyć do każdego rodzaju modelu (nie zakładamy na początku niczego dot. jego stuktury).
bd_Nomadland = exp.predict_parts(Nomadland, type='break_down', label=Nomadland.index[0])
#bd_interactions_Nomadland = exp.predict_parts(Nomadland, type='break_down_interactions', label="Nomadland+")
sh_Nomadland = exp.predict_parts(Nomadland, type='shap')
bd_Nomadland.plot() # wykres B
Jak widać, w przypadku Nomadland największe znaczenie miało to, że zdobył nagordę PGA (choć istotne okazały się również inne nagrody). Finalne przewidywanie wydaje się być dość dobre: mamy wartość 0.817 dla filmu, który wiemy że ostatecznie wygrał. Patrząc na średnią wartość: 0.175 widzimy, że Nomadland zdecydowanie 'odstaje' od średniej, a przecież w datasecie mamy zdecydowaną przewagę filmów, które Oscara nie zdobyły, więc nasz zwycięzca dość wyraźnie się wyróżnia.
Warto jednak zwrócić uwagę, że w przypadku BreakDown nie jest bez znaczenia w jakiej kolejności są podane sprawdzane cechy. W wziązku z tym popatrzmy również na Shapley Values, gdzie ten problem jest eliminowany poprzez liczenie średniej uwzględniającej wszystkie możliwe ułożenia cech:
sh_Nomadland.plot(bar_width = 16) #wykres C
Tutaj znowu mamy największy wpływ nagrody DGA - znowu widzimy, że informacja o jej zdobyciu mocno 'skłania' model w kierunku indentyfikacji Nomadland jako zwycięzcy Oscara. Ogólnie można zauważyć, że przy przewidywaniu warto spojrzeć na inne nagrody, które film zdobył lub nie - zdobycie nagród PGA i Złotego Globu rówienież 'pomogło' modelowi w poprawnej ocenie. Z kolei brak nominacji do nagrody SAG (best cast) 'zmylił' model i oddalił Nomadland od identyfikacji jako zwycięzca. Widocznie inne wygrane filmy miały więcej nominacji do Oscara niż Nomadland.
Można też spojrzeć jak ogólnie wpływały dane cechy na przewidywanie modelu - nie tylko na przykładzie Nomadland (tzn. czy np. duży budżet sprzyjał zwycięstwu)
pdp_num = exp.model_profile(type = 'partial', label="pdp")
ale_num = exp.model_profile(type = 'accumulated', label="ale")
pdp_num.plot(ale_num)
Calculating ceteris paribus: 100%|█████████████████████████████████████████████████████| 74/74 [00:04<00:00, 15.27it/s] Calculating ceteris paribus: 100%|█████████████████████████████████████████████████████| 74/74 [00:05<00:00, 14.55it/s] Calculating accumulated dependency: 100%|██████████████████████████████████████████████| 74/74 [00:09<00:00, 8.01it/s]
Wbrew pozorom budżet ponad średnią wcale nie sprzyjał wygranej. Co ciekawe, pozytywne opinie krytyków (Rotten Tomatoes) niekoniecznie sprzyjały wygranej. Wyraźnie widać również wzrost szans na wygraną razem ze wzrostem liczby nominacji do Oscara oraz zdecydowany wzrost szans przy wygranej DGA. Zdecydowanie na plus było również wygranie Złotego Globu dla najlepszego dramatu. Widać również, że romanse i fantasy nie robią furrory na Oscarach. Widocznie wśród wygranych filmów znalazły się filmy o tematyce sportowej i westerny (przy prawdopodonie niewielkiej liczby takich nominowanych), ponieważ według modelu prawdopodobieństwo wygranej przy takich gatunkach wzrasta.